package com.ydlclass.loadbalancer;

import com.ydlclass.YrpcBootstrap;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 模板方法设计模式：父类（抽象类）定义流程模板，将具体算法的实现延迟到子类。
 */
public abstract class AbstractLoadBalancer implements LoadBalancer {
    
    // key是服务名，value是selector对象。一个服务会匹配一个selector
    private Map<String, Selector> cache = new ConcurrentHashMap<>(8);

    /**
     * 该方法的作用是根据服务名和分组名称返回一个可用的服务节点
     */
    @Override
    public final InetSocketAddress selectServiceAddress(String serviceName,String group) {
        //加final是为了防止子类重写父类的模板方法

        // 1、优先从cache中获取一个选择器
        Selector selector = cache.get(serviceName);
        
        // 2、如果没有，就需要为这个service创建一个selector
        if (selector == null) {
            // 对于这个负载均衡器，内部应该维护服务列表作为缓存
            List<InetSocketAddress> serviceList = YrpcBootstrap.getInstance()
                .getConfiguration().getRegistryConfig().getRegistry().lookup(serviceName,group);
            
            // 提供一些算法负责选取合适的节点
            selector = getSelector(serviceList);
            
            // 将select放入缓存当中
            cache.put(serviceName, selector);
        }
        
        // 获取可用节点
        return selector.getNext();
    }
    

    //当节点动态上下线的时候需要进行reLoadBalance，更新选择器
    @Override
    public final synchronized void reLoadBalance(String serviceName,List<InetSocketAddress> addresses) {
        //为了确保线程安全，需要在方法上加synchronized关键字 
        // 我们可以根据新的服务列表生成新的selector
        cache.put(serviceName,getSelector(addresses));
    }
    

    /**
     * 由子类进行扩展
     * @param serviceList 服务列表
     * @return 负载均衡算法选择器
     */
    protected abstract Selector getSelector(List<InetSocketAddress> serviceList);
    
}
